/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions      */
/* are met:                                                                */
/*                                                                         */
/*    1. Redistributions of source code must retain the above copyright    */
/* notice, this list of conditions and the following disclaimer.           */
/*    2. Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the     */
/* documentation and/or other materials provided with the distribution.    */
/*                                                                         */
/* THIS SOFTWARE IS PROVIDED BY THE MOD_ATHENA PROJECT ``AS IS'' AND ANY   */
/* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       */
/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR      */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR        */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,     */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR      */
/* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT            */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "athd.h"

/* begin_privates */
static apr_hash_t *_cache = NULL;
static apr_pool_t *_cache_ppool = NULL;
static apr_pool_t *_cache_pool = NULL;

static athd_job * athd_job_alloc(apr_pool_t *pool);
static void athd_job_cache_expire();
/* end_privates */

static athd_job * 
athd_job_alloc(apr_pool_t *pool)
{
    athd_job *job;
    apr_pool_t *jpool;
    ASSERT(pool);
    apr_pool_create(&jpool, pool);
    ASSERT(jpool);
    job = (athd_job *) apr_pcalloc(jpool, sizeof(athd_job));
    ASSERT(job);
    job->pool = jpool;
    job->name = NULL;
    job->freq = ATHD_DEFAULT_FREQ;
    job->iter_lives = ATHD_JOB_ETERNAL;
    job->iter_count = 0;
    job->mail = ATH_OFF;
    job->mailaddrs = ath_list_ap_alloc(job->pool);
    job->replyto = NULL;
    job->mail_resend_throttle = 0;
    /*    job->engines = ath_list_ap_alloc(job->pool); */
    /* having just one engine is so much cleaner */
    job->engine = NULL;
    job->thread = NULL;
    job->response_q = ath_list_ap_alloc(job->pool);
    job->mutex = apr_pcalloc(job->pool, sizeof(pthread_mutex_t));
    pthread_mutex_init(job->mutex, NULL);
    apr_pool_cleanup_register(job->pool, job->mutex,
			      pthread_mutex_cleanup, apr_pool_cleanup_null);
    job->response_q_mutex = apr_pcalloc(job->pool, sizeof(pthread_mutex_t));
    apr_pool_cleanup_register(job->pool, job->response_q_mutex,
			      pthread_mutex_cleanup, apr_pool_cleanup_null);
    pthread_mutex_init(job->response_q_mutex, NULL);
    job->dienow = 0;
    return job;
}

athd_job *
athd_job_create(apr_pool_t *pool, const char *name)
{
    athd_job *job;
    ASSERT(pool && name);
    job = athd_job_alloc(pool);
    ASSERT(job);
    job->name = apr_pstrdup(job->pool, name);
    return job;
}

void
athd_job_destroy(athd_job **job)
{
    apr_pool_t *pool;
    pthread_mutex_lock((*job)->mutex);
    pool = (*job)->pool;
    pthread_mutex_unlock((*job)->mutex);
    apr_pool_destroy(pool);
    (*job) = NULL;
}

void
athd_job_cache_init(apr_pool_t *pool)
{
    apr_pool_t *subpool;
    ASSERT(pool);
    /* always use the same parent "pee" pool we started with */
    if (_cache_ppool) 
      {
	  athd_job_cache_expire();
	  apr_pool_destroy(_cache_ppool);
      }
    _cache_ppool = pool;
    apr_pool_create(&subpool, _cache_ppool);
    _cache_pool = subpool;
    _cache = apr_hash_make(_cache_pool);
}

static void 
athd_job_cache_expire()
{
    if (_cache)
      {
	  if (_cache_pool)
	      apr_pool_destroy(_cache_pool);
	  _cache_pool = NULL;
      }
    _cache = NULL;
}

athd_job * 
athd_job_add_by_name(athd_conf *conf, const char *str_job)
{
    athd_job *job;
    ASSERT(conf && str_job);
    if (str_job)
      {
	  job = athd_job_for_name(conf->jobs, str_job);
	  if (job)
	      athd_log(ATHD_DEBUG, ATHD_WARN_JOBEXST, job->name);
	  else
	    {
		job = athd_job_create(conf->pool, str_job);
		ath_list_ap_add(conf->pool, conf->jobs, job);
	    }
	  return job;  
      }
    return NULL;
}

athd_job * 
athd_job_for_name(ath_list *jobs, const char *str_job)
{
    int i;
    athd_job *job = NULL;
    ASSERT(jobs);
    if (str_job)
      {
	  if (_cache)
	      job = (athd_job *)
		apr_hash_get(_cache, str_job, APR_HASH_KEY_STRING);
	  else if (_cache_ppool)
	      athd_job_cache_init(_cache_ppool);

	  if (!job)
	    {
		ath_node *ctx;
		ath_list_rewind_r(jobs, &ctx);
		while ((job == NULL) && (ath_list_hasnext_r(jobs, &ctx)))
		  {
		      job = (athd_job *) ath_list_getnext_r(jobs, &ctx);
		      if ((strlen(str_job) != strlen(job->name))
			  || (strncmp(job->name, str_job, strlen(str_job)))
			  )
			  job = NULL;
		  }
		if ((_cache) && (job))
		    apr_hash_set(_cache, job->name, APR_HASH_KEY_STRING, job);
	    }
      }
    return job;
}

athd_job_type 
athd_job_type_from_str(const char *type)
{
    if ((strlen(type) == strlen(ATHD_JOB_STR_MON))
	&& (!strncasecmp(type, ATHD_JOB_STR_MON,
		     strlen(ATHD_JOB_STR_MON)))
	)
	return ATHD_JOB_MON;
    else if ((strlen(type) == strlen(ATHD_JOB_STR_MIR))
	     && (!strncasecmp(type, ATHD_JOB_STR_MIR, 
			  strlen(ATHD_JOB_STR_MIR)))
	     )
	return ATHD_JOB_MIR;
    else if ((strlen(type) == strlen(ATHD_JOB_STR_AGT))
	     && (!strncasecmp(type, ATHD_JOB_STR_AGT, 
			  strlen(ATHD_JOB_STR_AGT)))
	     )
	return ATHD_JOB_AGT;
    else
	return ATHD_JOB_NON;
}

char * 
athd_job_type_to_str(athd_job_type type)
{
    if (type == ATHD_JOB_MON)
	return ATHD_JOB_STR_MON;
    else if (type == ATHD_JOB_MIR)
	return ATHD_JOB_STR_MIR;
    else if (type == ATHD_JOB_AGT)
	return ATHD_JOB_STR_AGT;
    else
	return ATHD_JOB_STR_NON;
}

apr_status_t
athd_job_set_mail_on(athd_job *job, int flag)
{
    pthread_mutex_lock(job->mutex);
    job->mail = flag;
    pthread_mutex_unlock(job->mutex);
    return APR_SUCCESS;
}

apr_status_t
athd_job_add_mailaddr(athd_job *job, const char *addr)
{
    char *myaddr;
    myaddr = apr_pstrdup(job->pool, addr);
    ath_list_ap_add(job->pool, job->mailaddrs, myaddr);
    return APR_SUCCESS;
}

apr_status_t
athd_job_set_mailreplyto(athd_job *job, const char *addr)
{
    char *myaddr;
    myaddr = apr_pstrdup(job->pool, addr);
    pthread_mutex_lock(job->mutex);
    job->replyto = myaddr;
    pthread_mutex_unlock(job->mutex);
    return APR_SUCCESS;
}

apr_status_t
athd_job_set_mailrate(athd_job *job, const char *rate)
{
    char *pos, *myrate;
    /* base interval 1 second */
    apr_time_t usecs;
    ASSERT(job);
    ASSERT(rate);
    myrate = apr_pstrdup(job->pool, rate);
    ath_str_to_apr_time(myrate, &usecs);
    pthread_mutex_lock(job->mutex);
    job->mail_resend_throttle = usecs;
    pthread_mutex_unlock(job->mutex);
    return APR_SUCCESS;
}

/* used to allow multi engines but too messy to manage */
apr_status_t
athd_job_add_engine(athd_job *job, athd_engine *engine)
{
/*     ath_node *listctx; */
    pthread_mutex_lock(job->mutex);
    job->engine = engine;
/*     ath_list_rewind_r(job->engines, &listctx); */
/*     while ((ath_list_hasnext_r(job->engines, &listctx)) */
/* 	   && (engcmp != engine)) */
/* 	engcmp = (athd_engine *)  */
/* 	  ath_list_getnext_r(job->engines, &listctx); */
/*     if (engcmp != engine) */
/* 	ath_list_ap_add(job->pool, job->engines, engine); */
    pthread_mutex_unlock(job->mutex);
    return APR_SUCCESS;
}

/* used by engine_thread to give received lb data back to a job */
apr_status_t
athd_job_push_response(athd_request *req)
{
    ASSERT(req);
    ASSERT(req->job);
    ath_list_ap_add_tail(req->pool, req->job->response_q, req);
    return APR_SUCCESS;
}

athd_request *
athd_job_shift_response(athd_job *job)
{
    ath_node *ctx;
    athd_request *req;
    ath_list_rewind_r(job->response_q, &ctx);
    if (ath_list_hasnext_r(job->response_q, &ctx))
	req = ath_list_popnext_r(job->response_q, &ctx);
    else
	req = NULL;
    return req;
}

apr_status_t
athd_job_lock(athd_job *job)
{
    int rv;
    rv = pthread_mutex_lock(job->mutex);
    return rv;
}

apr_status_t
athd_job_unlock(athd_job *job)
{
    int rv;
    rv = pthread_mutex_unlock(job->mutex);
    return rv;
}

char *
athd_job_to_str(apr_pool_t *pool, athd_job *job)
{
    char *str;
    char *addrs = NULL;
    ASSERT(job);
    addrs = ath_list_str_join(pool, ", ", job->mailaddrs);
    str = apr_psprintf(pool,
		       "Job name: %s (%s)\n"
		       "  engine: %s\n"
		       "  type: %s\n"
		       "  frequency: %d secs\n"
		       "  iterations done: %ld\n"
		       "  mail enabled: %s\n"
		       "  mail addrs: %s\n"
		       "  mail reply-to: %s\n"
		       "  mail resend throttle: %d secs\n",
		       job->name,
		       job->dienow ? "dead" : "alive",
		       job->engine->name,
		       athd_job_type_to_str(job->type),
		       job->freq,
		       job->iter_count,
		       job->mail ? "yes" : "no",
		       addrs ? addrs : "(none)",
		       job->replyto ? job->replyto : "(not set)",
		       (apr_time_sec(job->mail_resend_throttle))
		       );
    return str;
}
